package com.weishi.pay.serviceImpl;

import java.io.File;
import java.io.FileInputStream;
import java.security.KeyStore;
import java.security.SecureRandom;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;
import java.util.UUID;

import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;

import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang.RandomStringUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.DefaultHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.BasicHttpClientConnectionManager;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;

import com.alibaba.dubbo.config.annotation.Reference;
import com.fasterxml.jackson.core.type.TypeReference;
import com.weishi.pay.api.constant.RedisCacheConstants;
import com.weishi.pay.api.dto.PaymentDto;
import com.weishi.pay.api.exception.PayErrors;
import com.weishi.pay.api.service.WexiPublicService;
import com.weishi.pay.common.ComUtils;
import com.weishi.pay.common.WebUtils;
import com.weishi.pay.common.XmlUtils;
import com.weishiinfo.commons.exception.BaseErrorCodes;
import com.weishinfo.center.user.rpc.api.WxAuthService;

import net.sf.json.JSONObject;

/**
 * 支付接口实现类
 *
 * @author liufeng
 * @create 2018-07-04 15:29
 **/
@Service
public class WeixinPublicServiceImpl implements WexiPublicService {
	protected Logger logger = LoggerFactory.getLogger(this.getClass());
	private static final String PREPAY_ID_REQUEST_URL = "https://api.mch.weixin.qq.com/pay/unifiedorder";
	private static final String CODE_REQUEST_URL = "https://open.weixin.qq.com/connect/oauth2/authorize";
	private static final String REFUD_REQUEST_URL = "https://api.mch.weixin.qq.com/secapi/pay/refund";
	/**
	 * openId请求URL
	 */
	private static final String OPEN_ID_REQUEST_URL = "https://api.weixin.qq.com/sns/oauth2/access_token";

	@Reference
	WxAuthService payWxAuthService;

	@Autowired
	ComUtils utils = new ComUtils();

	@Autowired
	private RedisTemplate<String, Object> redisTemplate;

	/**
	 * 统一下单
	 * 
	 * @param
	 * @param
	 * @param
	 * @param paymentDto
	 * @param paymentDto
	 */

	@Override
	public Map pay(PaymentDto paymentDto) {
		// 处理重复
		Object payno = redisTemplate.opsForValue()
				.get(RedisCacheConstants.PAY_REPEAT_SEND_CODE_PREFIX + paymentDto.getPayNo());
		if (payno != null) {
			return null;
		}
		logger.info("统一下单前台参数" + com.alibaba.fastjson.JSONObject.toJSONString(paymentDto) );

		String openId = payWxAuthService.getWxOpenIdByUid(paymentDto.getUserId());
		if(org.apache.commons.lang3.StringUtils.isEmpty(openId)){
			throw BaseErrorCodes.INVALID_SESSION_TOKEN.bornException();
		}
		Map<String, Object> parameterMap = new TreeMap<>();
		parameterMap.put("appid", utils.getAppid());
		parameterMap.put("body", "众享美服");
		parameterMap.put("mch_id", utils.getMch_id());
		parameterMap.put("nonce_str", DigestUtils.md5Hex(UUID.randomUUID() + RandomStringUtils.randomAlphabetic(10)));
		parameterMap.put("fee_type", "CNY");
		parameterMap.put("device_info", "WEB");
		parameterMap.put("out_trade_no", paymentDto.getPayNo());
		parameterMap.put("total_fee", paymentDto.getTotalFee());
		parameterMap.put("spbill_create_ip", paymentDto.getGetLocalAddr());
		parameterMap.put("notify_url", utils.getNotify_url());
		parameterMap.put("trade_type", "JSAPI");
		parameterMap.put("openid", openId);
		parameterMap.put("sign_type", "MD5");
		parameterMap.put("sign", utils.generateSign(parameterMap));
		logger.info("统一下单请求参数parameterMap>>>>>>>>>" + parameterMap.toString());
		String result = WebUtils.post(PREPAY_ID_REQUEST_URL, XmlUtils.toXml(parameterMap));
		Map<String, String> resultMap = XmlUtils.toObject(result, new TypeReference<Map<String, String>>() {
		});
		logger.info("统一下单返回参数resultMap>>>>>>>>>" + resultMap.toString());
		String return_code = resultMap.get("return_code");
		String prepayId = resultMap.get("prepay_id");
		String tradeType = resultMap.get("trade_type");
		String resultCode = resultMap.get("result_code");
		String err_code = resultMap.get("err_code");

		Map<String, Object> modelMap = new TreeMap<>();
		if (StringUtils.equals(return_code, "SUCCESS")) {
			if (StringUtils.equals(resultCode, "SUCCESS") && StringUtils.equals(tradeType, "JSAPI")) {
				modelMap.put("appId", utils.getAppid());
				modelMap.put("timeStamp", System.currentTimeMillis());
				modelMap.put("nonceStr",
						DigestUtils.md5Hex(UUID.randomUUID() + RandomStringUtils.randomAlphabetic(30)));
				modelMap.put("package", "prepay_id=" + prepayId);
				modelMap.put("signType", "MD5");
				modelMap.put("paySign", utils.generateSign(modelMap));
				modelMap.put("postPayUrl", utils.getNotify_url());
			} else {
				switch (err_code) {
				case "NOAUTH":
					throw PayErrors.NOAUTH.bornException();
				case "NOTENOUGH":
					throw PayErrors.NOTENOUGH.bornException();
				case "ORDERPAID":
					throw PayErrors.ORDERPAID.bornException();
				case "ORDERCLOSED":
					throw PayErrors.ORDERCLOSED.bornException();
				case "SYSTEMERROR":
					throw PayErrors.SYSTEMERROR.bornException();
				case "APPID_NOT_EXIST":
					throw PayErrors.APPID_NOT_EXIST.bornException();
				case "MCHID_NOT_EXIST":
					throw PayErrors.MCHID_NOT_EXIST.bornException();
				case "APPID_MCHID_NOT_MATCH":
					throw PayErrors.APPID_MCHID_NOT_MATCH.bornException();
				case "LACK_PARAMS":
					throw PayErrors.LACK_PARAMS.bornException();
				case "OUT_TRADE_NO_USED":
					throw PayErrors.OUT_TRADE_NO_USED.bornException();
				case "SIGNERROR":
					throw PayErrors.SIGNERROR.bornException();
				case "XML_FORMAT_ERROR":
					throw PayErrors.XML_FORMAT_ERROR.bornException();
				case "REQUIRE_POST_METHOD":
					throw PayErrors.REQUIRE_POST_METHOD.bornException();
				case "POST_DATA_EMPTY":
					throw PayErrors.POST_DATA_EMPTY.bornException();
				case "NOT_UTF8":
					throw PayErrors.NOT_UTF8.bornException();
				}

			}

		} else {
			logger.error("统一下单失败return_msg=" + resultMap.get("return_msg"));
			throw PayErrors.PAY_PAYORDER.bornException();

		}
		logger.info("下单返回前台参数modelMap=" + modelMap);
		return modelMap;

	}

	/**
	 * 支付回调
	 * 
	 * @param
	 * @param
	 * @param
	 */
	@Override
	public Map<String, String> getRequestMap(String xml) {

		Map<String, String> resultMap = XmlUtils.toObject(xml, new TypeReference<Map<String, String>>() {
		});

		return resultMap;
	}

	/**
	 * 获取OpenID
	 *
	 * @param code code值
	 * @return OpenID
	 */
	private String getOpenId(String code) throws Exception {
		String wxWebAccessTokenUrl = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=%s&secret=%s&code=%s&grant_type=authorization_code";
		String url = String.format(wxWebAccessTokenUrl, utils.getAppid(), utils.getAppSercet(), code);
		logger.info("获取OpenId url=" + url);
		RestTemplate restTemplate = new RestTemplate();
		try {
			String resp = restTemplate.getForObject(url, String.class);
			JSONObject obj = JSONObject.fromObject(resp);
			logger.info("调用微信接口获取operid返回：{}", obj);
			return obj.containsKey("openid") ? obj.getString("openid") : "";
		} catch (RestClientException e) {
			e.printStackTrace();

		}
		return "";
	}

	/**
	 * 发起退款请求
	 * 
	 * @param paymentDto
	 */

	@Override
	public Map refund(PaymentDto paymentDto) {
		Map<String, Object> requestMap = new HashMap<>();
		// 组装请求参数
		requestMap.put("appid", utils.getAppid());
		requestMap.put("mch_id", utils.getMch_id());
		requestMap.put("nonce_str", utils.generateNonceStr());
		requestMap.put("sign_type", "MD5");
		requestMap.put("out_trade_no", paymentDto.getPayNo());
		requestMap.put("out_refund_no", paymentDto.getRefundNo());
		requestMap.put("total_fee", paymentDto.getTotalFee().toString());
		requestMap.put("refund_fee", paymentDto.getRefundFee().toString());
		requestMap.put("refund_fee_type", "CNY");
		requestMap.put("refund_desc", paymentDto.getRefundDesc());
		requestMap.put("refund_account", "REFUND_SOURCE_UNSETTLED_FUNDS");

		requestMap.put("notify_url", utils.getRefund_url());

		requestMap.put("sign", utils.generateSign(requestMap));
		logger.info("退款请求参数requestMap=" + requestMap.toString());
		String repString;
		try {
			repString = doRefund(REFUD_REQUEST_URL, XmlUtils.toXml(requestMap));
			Map<String, String> responseMap = utils.processResponseXml(repString);

			logger.info("退款返回参数responseMap=" + responseMap.toString());

			Map retutnMap = new HashMap();
			if (responseMap.get("return_code").equals("FAIL")) {
				retutnMap.put("flag", "FAIL");
				retutnMap.put("msg", "申请退款业务受理失败");
				return retutnMap;
			}
			if (responseMap.get("return_code").equals("SUCCESS")) {
				if (responseMap.get("result_code").equals("SUCCESS")) {
					retutnMap.put("flag", "SUCCESS");
					retutnMap.put("msg", "退款成功");
					return retutnMap;
				} else {
					logger.info(" 提交退款业务失败err_code_des=" + responseMap.get("err_code_des"));
					retutnMap.put("flag", "SUCCESS");
					retutnMap.put("msg", responseMap.get("err_code_des"));
					return retutnMap;
				}
			}
		} catch (Exception e) {
			throw PayErrors.REFUND_COMMON_ERROR.bornException();
		}

		return null;
	}


	public String doRefund(String url, String data) throws Exception {
		/**
		 * 注意PKCS12证书 是从微信商户平台-》账户设置-》 API安全 中下载的
		 */
		BasicHttpClientConnectionManager connManager;
		KeyStore keyStore = KeyStore.getInstance("PKCS12");
		String cerPath = getClass().getResource("/")+"cert/apiclient_cert.p12";
		logger.info("证书路劲"+cerPath);
		// 本地测试
		//String cerPath = System.getProperty("user.dir") + "\\cert\\apiclient_cert.p12";
		FileInputStream instream = new FileInputStream(new File(cerPath));// P12文件目录
		try {

			keyStore.load(instream, utils.getMch_id().toCharArray());// 这里写密码..默认是你的MCHID
		} finally {
			instream.close();
		}

		// 实例化密钥库 & 初始化密钥工厂
		KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
		kmf.init(keyStore, utils.getMch_id().toCharArray());
		// 创建 SSLContext
		SSLContext sslContext = SSLContext.getInstance("TLS");
		sslContext.init(kmf.getKeyManagers(), null, new SecureRandom());

		SSLConnectionSocketFactory sslConnectionSocketFactory = new SSLConnectionSocketFactory(sslContext,
				new String[] { "TLSv1" }, null, new DefaultHostnameVerifier());

		connManager = new BasicHttpClientConnectionManager(RegistryBuilder.<ConnectionSocketFactory>create()
				.register("http", PlainConnectionSocketFactory.getSocketFactory())
				.register("https", sslConnectionSocketFactory).build(), null, null, null);

		HttpClient httpClient = HttpClientBuilder.create().setConnectionManager(connManager).build();

		HttpPost httpost = new HttpPost(url); // 设置响应头信息
		httpost.addHeader("Connection", "keep-alive");
		httpost.addHeader("Accept", "*/*");
		httpost.addHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
		httpost.addHeader("Host", "api.mch.weixin.qq.com");
		httpost.addHeader("X-Requested-With", "XMLHttpRequest");
		httpost.addHeader("Cache-Control", "max-age=0");
		httpost.addHeader("User-Agent", "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0) ");
		httpost.setEntity(new StringEntity(data, "UTF-8"));
		HttpPost httpPost = new HttpPost(url);

		RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(utils.getHttpReadTimeoutMs())
				.setConnectTimeout(utils.getHttpConnectTimeoutMs()).build();
		httpPost.setConfig(requestConfig);

		StringEntity postEntity = new StringEntity(data, "UTF-8");
		httpPost.addHeader("Content-Type", "text/xml");
		httpPost.addHeader("User-Agent", "wxpay sdk java v1.0 " + utils.getMch_id()); // TODO: 很重要，用来检测 sdk
																						// 的使用情况，要不要加上商户信息？
		httpPost.setEntity(postEntity);

		HttpResponse httpResponse = httpClient.execute(httpPost);
		HttpEntity httpEntity = httpResponse.getEntity();
		return EntityUtils.toString(httpEntity, "UTF-8");

	}

}
